/*
 * Created on 2007-12-9
 *
 * TODO
 */
package jacky.lanlan.song.pattern;

import jacky.lanlan.song.pattern.factory.PropertyValueGenerator;
import jacky.lanlan.song.pattern.factory.PropertiesCfg;
import jacky.lanlan.song.reflection.AnnotationUtils;
import jacky.lanlan.song.util.Assert;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * 关于模式的工具类。
 * 
 * @author Jacky.Song
 */
public abstract class PatternUtils {

	private static final ConcurrentMap<String, PropertyValueGenerator<?>> ANNOTATION_FACTORY_CACHE = new ConcurrentHashMap<String, PropertyValueGenerator<?>>();

	/**
	 * 通过标注装载对象。
	 * <p>
	 * 该方法通过构造一个工厂({@link AnnotationFactory})来装载对象。<i>工厂被创建后将被缓存。</i>
	 * <p>
	 * 适用于该方法的标注必须有至少一个属性的签名符合下列格式：<br/>
	 * <code>Class &lt;? extends AnnotationFactory<<i>Foo</i>>> <i>bar</i>();</code>
	 * <br/> 其中<code>Foo</code>是要装载的对象类型，<code>bar</code>是属性名。
	 * 
	 * @see Factory
	 * @param element
	 *          可以被标注的元素
	 * @param annotation
	 *          标注
	 * @param attriName
	 *          属性名
	 * @return 通过工厂所创建的对象
	 */
	public static Object loadObjectFromAnnotation(AnnotatedElement element, Class<? extends Annotation> annotation,
			String attriName) {
		Object factoryClass = AnnotationUtils.getValue(element.getAnnotation(annotation), attriName);

		// 检查属性值是否符合要求
		Assert.notNull(factoryClass, "没有在 [" + element + "] 中找到标注 [" + annotation + "]");

		Assert.isTrue(factoryClass instanceof Class<?>, "属性 [" + attriName + "] 不是一个Class");

		// 缓存
		String key = ((Class<?>) factoryClass).getName();
		if (!ANNOTATION_FACTORY_CACHE.containsKey(key)) {
			Object factory = null;
			try {
				factory = ((Class<?>) factoryClass).newInstance();
			}
			catch (Exception e) {
				throw new RuntimeException("不能创建 AnnotationFactory ：" + e);
			}

			Assert.isTrue(factory instanceof PropertyValueGenerator<?>, "属性 [" + attriName + "] 不是一个 AnnotationFactory Class");
			ANNOTATION_FACTORY_CACHE.putIfAbsent(key, (PropertyValueGenerator<?>) factory);
		}

		// 通过，开始创建对象
		Object object = null;
		try {
			object = ANNOTATION_FACTORY_CACHE.get(key).generate(element.getAnnotation(annotation));
		}
		catch (Exception e) {
			throw new RuntimeException("不能创建对象：" + e);
		}
		return object;
	}

	/**
	 * 和#loadObjectFromAnnotation()类似，该方法寻找标注里面名为"factory"的属性，通过它装载对象。
	 */
	public static Object loadObjectFromAnnotation(AnnotatedElement element, Class<? extends Annotation> annotation) {
		return loadObjectFromAnnotation(element, annotation, "factory");
	}

	/**
	 * 根据 {@link PropertiesCfg} 的配置，读取Properties文件的值。
	 * 
	 * @param element
	 *          被 {@link PropertiesCfg} 标注的对象
	 * @return Properties文件的属性值数组，值按照 {@link PropertiesCfg} 的keys属性声明顺序排列
	 * @see PropertiesCfg
	 */
	public static String[] loadPropertyFromAnnotation(AnnotatedElement element) {
		return (String[]) loadObjectFromAnnotation(element, PropertiesCfg.class);
	}
	
}
